﻿

/*
类名：Combobox
描述：多功能弹出下拉框
创见者：RANDY
最后修改日期：2006-05-25

构造函数的参数：
className 	为您定义该类的变量名  
target 		为目标控件的ID，既文本框ID

属性：
TimeInterval = 500;	//查询的时间间隔，用豪秒表示
TextFormat = "V1";	//返回值的格式 V1 代表对应的第一个值 V2,V3
ValueFormat = "V1";		//文本框的tag值的格式
ListType = ListType.Combobox;	//显示列表的类型，表格、单列
DataType = DataType.XmlDocument;		//数据类型
DataSource = null;			//数据源
HeadColumn;	//表格头列名，是一个数组  var column = new Array();  当显示状态为Table才起作用
DisplayHead = true;	//是否显示表头
ListWidth = 400;		//下拉列表的宽度
ListHeight = 200;

方法：
Init()
DataBind()
InsertItem();			您定义了多少列你就使用多少个参数
className.ShowList();	如果有多个文本框想同时使用一个该下拉框就可以使用。 eg:onclick = className.ShowList(this);

事件：
OnTextChangeEvent = "";//过滤事件，委托用外部处理  写入方法名即可  LoadxmlData(val)
OnItemClickEvent = "";		//列表框单击事件
	
*/

function debug(txt) {

    return;

    if ($("#debug").length == 0) {
        var textarea = document.createElement("textarea");
        textarea.id = "debug";
        textarea.style.width = "100%";
        textarea.style.height = "100px";

        $("body").prepend(textarea);
    }

    $("#debug").val(txt + "\r\n" + $("#debug").val());
}


String.prototype.Trim = function () {
    return this.replace(/(^\s*)|(\s*$)/g, "");
}

//弹出方向枚举
var PopupDirection =
{
    LeftTop: 1,
    RightTop: 2,
    LeftBottom: 3,
    RightBottom: 4,
    None: 5
}

//下拉列表的显示类型
var ListType =
{
    DropDownList: 1, 	//下拉框，不会根据输入的内容过滤
    Combobox: 2			//复合下拉框，根据输入的内容过滤无效行
}

//数据类型
var DataType =
{
    Custom: 1, 	//用户自定义
    Xml: 2, //XML文档
    List: 3, 		//列表框	
    Json: 4
}

var DataRequest;

$.fn.combobox = function (options) {
    debug("init combobox");
    var defaults = {
        //公有属性，由用户自己设置
        OnTextChangeEvent: null, //过滤事件，委托用外部处理  写入方法名即可  LoadxmlData(val,src)  val 为关键字 src 当前对象
        OnItemClickEvent: "", 	//列表框单击事件  function OnItemClickEvent(_oTargetControl, { text: text, value: value, tag: selectedValue })
        TimeInterval: 500, 	//查询的时间间隔，用豪秒表示
        TextFormat: "V1", 	//返回值的格式 V1 代表对应的第一个值 V2,V3
        ValueFormat: "V1", 		//文本框的tag值的格式
        TagObject: null, //tag值存储对象，一般为hidden textbox
        RemoteUrl: null, //远程数据请求URL，如果该URL存在值，则采用动态ajax请求数据
        ListType: ListType.Combobox, 	//显示列表的类型，表格、单列
        DataType: DataType.Xml, //数据类型
        DataSource: null, 		//数据源
        DataLoadOnce: false,     //数据是否仅加载一次
        HeadColumn: [{ name: "head", display: false}], 	//表格头列名，是一个数组  var column : new Array(),  当显示状态为Table才起作用
        DisplayHead: false, 	//是否显示表头
        ListWidth: 400, 		//下拉列表的宽度
        ListHeight: null,
        MustMatching: true, 	//是否必须匹配
        KeyWordColumn: -1, 	//查询的列 如果这列没设置将不会选择默认的值
        ColumnsMapsNodeName: "", 	//采用XML为数据源列对应的节点名称  格式：nodeName1|nodeName2|nodeName3 ……
        Buttons: null  //底部现实的按钮
    } //默认参数
    var opt = $.extend({}, defaults, options);

    var defaultsHeadColumn = { name: "", width: "", display: true} //头部默认属性
    //重置表头属性
    if (opt.HeadColumn != null) {
        for (var i = 0; i < opt.HeadColumn.length; i++) {
            var opts = $.extend({}, defaultsHeadColumn, opt.HeadColumn[i]);
            opt.HeadColumn[i] = opts;
        }
    }


    var rnd = Math.round(Math.random() * 10000000);
    var _LayerID = "Commbobox_Layer_" + rnd;
    var _DivHtml = "";
    _DivHtml += "<div id='" + _LayerID + "' ";
    _DivHtml += "	onmouseover = '' onmouseout = ''";
    _DivHtml += " 	class='cmbLayer' style='display:none;position:absolute; '>";
    _DivHtml += " <div class='loading' style='display:none; '>";
    _DivHtml += "Loading</div></div>";
    $("body").append(_DivHtml);

    //控件的内部对象
    var _oTargetControl = $(this).get(0); 		//文本框对象
    var _oLayer = document.getElementById(_LayerID); 			//弹出框对象  
    var _oLoading = $(".loading", _oLayer); //等待的图层对象
    var _PopupDirection = PopupDirection.None; //弹出的方向
    var _inLayer = false; //鼠标当前位置是否在弹出框内

    $(_oTargetControl).attr("autocomplete", "off");

    //	文本框的位置
    var _tagLeft;
    var _tagTop;
    var _tagHeight;
    var _tagWidth;

    //计数器的ID，作用是用来指定时间查询
    var _iTimeID = -1;

    //表格属性
    var _oTable;
    var _TableId = "Commbobox_Table_" + rnd;
    var _SelectedIndex = -1; //当前选中的行	
    var _HasInit = false; 	//是否已初始化了内容 
    var _DataRowIndex = 0; 	//数据行的起始位置
    var _HasSelect = false; //用户是否选择了下拉框的内容	
    var cst_keyWord = "$$$$"; //常量


    //为页面添加单击事件，主要用于隐藏弹出框
    $(document).click(function (src) {
        var obj = event.srcElement;
        if (_inLayer == false && obj != _oTargetControl) {
            HiddenList();
            //_oLayer.style.display = "none"; ;
        }
    });

    $(_oLayer).mouseover(function () { _inLayer = true; });
    $(_oLayer).mouseout(function () { _inLayer = false; });

    //文本框点击事件
    $(_oTargetControl).bind("click focus", function () {
        ShowList();
    });

    debug("new combobox() 7");


    //初始页页面控件信息
    var Init = function () {
        debug("init");

        if (_HasInit) return; //已初始了内容就不需要再初始
        $(_oLayer).css("width", opt.ListWidth);
        $(_oLayer).css("height", opt.ListHeight);
        _DataRowIndex = opt.DisplayHead ? 1 : 0;

        InitTable();

        $(_oTargetControl).keyup(function () {
            TargetControl_KeyDown();
        }); //初始点击事件

        _HasInit = true;

        //初始按钮

        if (opt.Buttons != null) {
            var footer = document.createElement("div");
            footer.className = "footer";
            for (var i = 0; i < opt.Buttons.length; i++) {
                var btn = opt.Buttons[0];
                var button = document.createElement("a");
                button.href = "javascript:void(0)";
                button.innerHTML = btn.text;
                $(button).click(function () {
                    btn.event();
                });
                $(footer).append(button);
            }
            $(_oLayer).append(footer);
        }
    }

    //根据输入的内容过滤无用的项
    //参数：MustExecute 是否强制执行该函数
    //		customKeyWord 自定义查询关键字，如果该关键字不为空，那么就按照它来查询
    var LoadData = function (MustExecute, customKeyWord) {
        debug("LoadData");

        //下拉框不过滤
        if (opt.ListType == ListType.DropDownList) return;
        MustExecute = MustExecute == null ? false : MustExecute;

        if (event != null && (event.keyCode == 13 || event.keyCode == 38 || event.keyCode == 40))
            return;


        //_oLoading.show();//显示等待信息        
        var keyWord = $(_oTargetControl).val(); //查询的关键字

        if ((keyWord == "" && opt.TagObject != undefined)
        || (keyWord == "" && opt.TagObject != null))
            opt.TagObject.value = "";


        //加载数据有三种方式，一、采用外部委托事件加载 二、采用ajax加载 三、在数据源中加载数据

        //如果有外部事件，使用外部事件来处理过滤,委托	
        if (opt.OnTextChangeEvent != null && opt.OnTextChangeEvent != "") {
            opt.OnTextChangeEvent(keyWord, DataBind); //参数 关键字，回调绑定函数

            _oLayer.scrollTop = 0;
            SelectedItem(_DataRowIndex);
            _oLoading.hide();
            return;
        }

        //采用ajax方式请求数据,如果仅加载一次，则不多次加载数据
        if (opt.RemoteUrl != null) {
            debug("hasdata: " + opt.DataLoadOnce && opt.DataSource != null);
            if (opt.DataLoadOnce && opt.DataSource != null) {
                debug(" opt.DataLoadOnce = true &&  opt.DataSource != null");
            }
            else {

                var url = opt.RemoteUrl + encodeURIComponent(keyWord) + "&rnd=" + Math.random();
                debug("ajax request: " + url);

                $.ajax({
                    type: "get",
                    url: url,
                    data: "",
                    dataType: "xml",
                    complete: function (XHR, TS) {

                    },
                    success: function (xmlDoc) {
                        debug("ajax request success: " + url);

                        DataBind(xmlDoc);
                        _oLoading.hide();

                        opt.DataSource = xmlDoc;
                    },
                    error: function (XMLHttpRequest, status, error) {
                        debug("ajax request error: " + url);
                        setPosition(); //设置好现实位置,IE 不兼容 success
                    }
                });

                debug("ajax request return;");
                return;
            }
        }

        //使用已加载的数据过滤
        DataBind(opt.DataSource);
        _oLayer.scrollTop = 0;
        _oLoading.hide();
        _iTimeID = -1; //执行完毕计数器还原
    }

    //绑定数据
    var DataBind = function (data) {
        debug("run databind");
        Init();
        ClearItem(); //清除原有的数据行

        var keyword = $(_oTargetControl).val();

        //通过json的数据方式加载
        if (opt.DataType == DataType.Json) {
            var jsondata = data;
            for (var i = 0; i < jsondata.length; i++) {
                var cellValues = new Array();
                var itemValues = "";
                for (var n = 0; n < jsondata[i].length; n++) {
                    cellValues.push(jsondata[i][n]);
                    itemValues += jsondata[i][n] + ",";
                }

                //如果是一次加载，则在数据源里面过滤
                if (!opt.DataLoadOnce || (opt.DataLoadOnce && itemValues.indexOf(keyword) != -1))
                    InsertItem(cellValues);
            }
            setPosition(); //设置好现实位置
            return;
        }

        //end 通过xml的数据方式加载
        if (opt.DataType == DataType.Xml) {
            var xmldoc = data;
            //debug("databind - xml");
            var root;
            var nodeRow;

            root = xmldoc.firstChild;
            if (root == null) return; //alert(opt.options.ClassName + "出错！数据源中没有数据");           
            while (root.nodeType != 1) root = root.nextSibling;

            nodeRow = root.childNodes;

            for (var i = 0; i < nodeRow.length; i++) {
                if (nodeRow[i].nodeName == "#text") continue; //兼容chrome
                //debug("databind - xml - " + nodeRow[i].nodeName);

                nCells = nodeRow[i].childNodes;

                var cellValues = new Array(); //列值数组
                var itemValues = ""; //行的值集合
                var jsonVal = ""; //行json格式的值

                if (opt.ColumnsMapsNodeName == "") {
                    //绑定所有列
                    for (var n = 0; n < nCells.length; n++) {

                        if (nCells[n].nodeName == "#text") continue; //兼容chrome
                        //debug("databind - xml - cell - " + nCells[n].nodeName);
                        cellValues.push($(nCells[n]).text());
                        itemValues += $(nCells[n]).text() + ",";
                    }
                }
                else {
                    //把列映射到节点的名称
                    var tNodes = opt.ColumnsMapsNodeName.split("|");
                    //绑定所有列
                    var val = "";

                    for (var n = 0; n < tNodes.length; n++) {
                        val = $(tNodes[n], nodeRow[i]).text();
                        cellValues.push(val);
                        itemValues += val + ",";

                        jsonVal += "\"" + tNodes[n] + "\":\"" + val + "\","
                    }
                    if (jsonVal != "") jsonVal = "{" + jsonVal.substring(0, jsonVal.length - 1) + "}";
                }
                //如果是一次加载，则在数据源里面过滤
                if (!opt.DataLoadOnce || (opt.DataLoadOnce && itemValues.indexOf(keyword) != -1))
                    InsertItem(cellValues, jsonVal);
            }
        }

        setPosition(); //设置好现实位置
    }

    //验证文本框内的数据是否有效
    var ValidateInput = function () {
        //alert(opt.MustMatching);
        if (opt.MustMatching) {
            //alert("2" + _HasSelect);
            //有关键字或内容将不会被清空
            if (_HasSelect == false && _oTargetControl.KeyWord == "") {
                _oTargetControl.Tag = "";
                _oTargetControl.value = "";
                _oTargetControl.SelectedValue = "";
            }
        }
        else {
            if (_HasSelect == false && _oTargetControl.KeyWord == "") {
                opt.TagObject.value = "";
                _oTargetControl.Tag = "";
                _oTargetControl.SelectedValue = "";
            }
        }

        if (_oTargetControl.value && _oTargetControl.value.Trim() == "")
            _oTargetControl.Tag = "";


        var obj = event.srcElement;
        //alert(obj.tagName);
        //opt.HiddenList();
    }



    //初始显示表格
    var InitTable = function () {
        var tbl = document.createElement("TABLE");
        tbl.id = _TableId;
        _oLayer.appendChild(tbl);
        _oTable = tbl;

        //是否需要添加表头
        if (_oTable != null && opt.HeadColumn != null) {
            var tr = document.createElement("tr");
            tr.className = "head";
            for (var i = 0; i < opt.HeadColumn.length; i++) {
                var td = document.createElement("td");
                td.innerHTML = opt.HeadColumn[i].name;
                $(td).css("width", opt.HeadColumn[i].width);

                if (!opt.HeadColumn[i].display) {
                    td.style.display = "none";
                }

                tr.appendChild(td);
            }
            if (!opt.DisplayHead) $(tr).hide();
            $(_oTable).append(tr);
        }
    }

    //返回值到所控制的对象
    //参数：e 表示点击的行
    var ItemClick = function (rIndex) {
        if (rIndex == -1)
            return;

        var tr = _oTable.rows[rIndex];
        var text = opt.TextFormat;
        var value = opt.ValueFormat;
        var jsonVal = tr.jsonVal;
        var selectedValue = "";
        var keyWord = "";

        for (var i = 0; i < tr.cells.length; i++) {
            text = text.replace("V" + i, tr.cells[i].innerHTML)
            value = value.replace("V" + i, tr.cells[i].innerHTML)
            selectedValue += tr.cells[i].innerHTML + ",";
            //找出查询列的值
            if (i + 1 == opt.KeyWordColumn) {
                keyWord = tr.cells[i].innerHTML;
            }
        }

        _HasSelect = true;

        //是否使用外部事件处理
        if (opt.OnItemClickEvent != "" && opt.OnItemClickEvent != null) {
            try {
                var jsonData = eval("(" + jsonVal + ")");
                opt.OnItemClickEvent(_oTargetControl, { text: text, value: value, tag: selectedValue, json: jsonData });
            }
            catch (e) {
                //alert("onListClick 定义的" + opt.OnItemClickEvent + "方法不存在或有错误！\n" + e.message);
                return;
            }
        }
        else {
            _oTargetControl.value = text;
            if (opt.TagObject != undefined && opt.TagObject != null) {
                opt.TagObject.value = value;
            }
        }

        //选中了设置相应的值
        SelectedItem(rIndex);
        _oTargetControl.KeyWord = keyWord;
        _oTargetControl.Tag = value;
    }

    //选中某行，只改变样式，不做其他操作
    var SelectedItem = function (rIndex) {
        if (opt.DisplayHead && rIndex == 0) return;

        $("tr", _oTable).removeClass("selected");
        $("tr", _oTable).eq(rIndex).addClass("selected");
        _SelectedIndex = rIndex;
    }

    /*
    插入行
    参数：iIndex 行号
    */
    var InsertItem = function (cellValues, jsonVal) {
        debug("insertItem");
        if (_oTable == null) InitTable;

        var newTr = document.createElement("tr");
        $(newTr).css("cursor", "hand");
        newTr.jsonVal = jsonVal;

        //添加事件到行
        $(newTr).click(function () {
            ItemClick($(this).index());
            HiddenList();
        });
        $(newTr).mouseover(function () {
            SelectedItem($(this).index());
        });

        //添加列
        for (var i = 0; i < cellValues.length; i++) {
            var td = document.createElement("td");
            td.innerHTML = cellValues[i];

            if (!opt.HeadColumn[i].display) {  //隐藏不需要的列
                td.style.display = "none";
            }

            newTr.appendChild(td);
        }

        $(_oTable).append(newTr)
    }

    //清除所有行
    var ClearItem = function () {
        $("tr[class!=head]", _oTable).remove();
    }

    //隐藏下拉列表
    var HiddenList = function () {
        debug("HiddenList");
        _oLayer.style.display = "none";
    }

    //显示下拉列表
    var ShowList = function () {
        debug("run showlist");
        Init();

        if (_oTargetControl.KeyWord == null) {
            _oTargetControl.KeyWord = "";
            _HasSelect = true;
        }

        debug("run showlist - LoadData");
        //加载数据
        LoadData(true);
    }

    //设置图层位置
    var setPosition = function () {
        var pop = GetPopupDirection(); //获取控件的最新位置
        $(_oLayer).css("left", pop.left);
        $(_oLayer).css("top", pop.top);

        debug("setPosition _oLayer.show");
        $(_oLayer).show(); //显示下拉框图层
    }

    //文本框点击事件
    var TargetControl_KeyDown = function () {
        debug("run - TargetControl_KeyDown");
        //tab
        if (event.keyCode == 9) {
            HiddenList();
            return;
        }
        //回车
        if (event.keyCode == 13) {
            ItemClick(_SelectedIndex);
            HiddenList();
            event.returnValue = false;
            return;
        }
        //上
        if (event.keyCode == 38) {
            if (_SelectedIndex > _DataRowIndex) SelectedItem(_SelectedIndex - 1);
            return;
        }
        //下
        if (event.keyCode == 40) {
            if (_SelectedIndex < _oTable.rows.length - 1) SelectedItem(_SelectedIndex + 1);
            return;
        }
        //删除按钮
        if (event.keyCode == 8 || event.keyCode == 46) {
            if (_oTargetControl.value.Trim() == "") _oTargetControl.Tag = "";
        }

        //输入其他字符，重新加载数据,需要延迟加载，反正快速录入
        //每次执行该函数都在指定的时间后
        if (_iTimeID != -1) {
            window.clearTimeout(_iTimeID); 	//计时器复位
        }
        _iTimeID = window.setTimeout(function () {
            LoadData(true);
        }, opt.TimeInterval);

        //LoadData(true);

        //如果初始文本被修改了，那么他的KeyWord状态也被清空
        _HasSelect = false;
        _oTargetControl.KeyWord = "";
    }

    //获取弹出的方向
    var GetPopupDirection = function () {
        var position = $(_oTargetControl).position();
        _tagTop = position.top;
        _tagLeft = position.left;
        _tagHeight = $(_oTargetControl).outerHeight();
        _tagWidth = $(_oTargetControl).outerWidth();

        var listHeight = $(_oLayer).outerHeight();

        //?????????
        var bHeight = GetBrowserHeight();
        var bWidth = document.body.offsetWidth;
        var scrTop = document.body.scrollTop;

        var dVertical;  //?????   1 ? 0 ?
        var dParallel;  //?????   1 ? 0 ?

        if ((_tagTop - scrTop + listHeight) < bHeight) {
            dParallel = 0;
        }
        else if (_tagTop - scrTop > listHeight) {
            dParallel = 1;
        }
        else {
            dParallel = 0;
            //不够位置弹出则在左下放弹出
            _PopupDirection = PopupDirection.LeftBottom;
            //return;
        }

        //?????
        if ((_tagLeft + opt.ListWidth) < bWidth) {
            dVertical = 0;
        }
        else if (_tagLeft + opt.ListWidth > bWidth) {   //?????
            dVertical = 1;
        }
        else {   //??????
            dVertical = 0;
            _PopupDirection = PopupDirection.LeftBottom;
            //return;
        }

        if (dParallel == 1 && dVertical == 1)
            _PopupDirection = PopupDirection.LeftTop;
        if (dParallel == 1 && dVertical == 0)
            _PopupDirection = PopupDirection.RightTop;
        if (dParallel == 0 && dVertical == 1)
            _PopupDirection = PopupDirection.LeftBottom;
        if (dParallel == 0 && dVertical == 0)
            _PopupDirection = PopupDirection.RightBottom;

       // alert();

        var nLeft, nTop;
        //alert(s);
        //弹出信息框
        switch (_PopupDirection) {
            //???                                                                                                                                                      
            case PopupDirection.LeftTop:
                nLeft = _tagLeft - (opt.ListWidth - _tagWidth);
                nTop = _tagTop - listHeight;
                break;
            //???                                                                                                                                                          
            case PopupDirection.RightTop:
                nLeft = _tagLeft;
                nTop = _tagTop - listHeight;
                break;

            //???                                                                                                                                                      
            case PopupDirection.LeftBottom:
                nLeft = _tagLeft - (opt.ListWidth - _tagWidth);
                nTop = _tagTop + _tagHeight;
                break;
            //???                                                                                                                                                      
            case PopupDirection.RightBottom:
                nLeft = _tagLeft;
                nTop = _tagTop + _tagHeight;
                break;
            //???                                                                                                                                                      
            case PopupDirection.None:
                nLeft = _tagLeft - (opt.ListWidth - _tagWidth);
                nTop = _tagTop + _tagHeight;
                break;
        }

       // alert(nLeft);

        return { left: nLeft, top: nTop };
    }


    //获取浏览器的高度
    var GetBrowserHeight = function () {
        var contentHeight;

        if (document.documentElement.clientHeight) {
            contentHeight = document.documentElement.clientHeight; //IE6
        } else {
            contentHeight = document.body.clientHeight; //IE5.x
        }

        return contentHeight;
    }

}
